//
//  MethodViewController.h
//  ReviewDemo
//
//  Created by 龙 on 2018/3/22.
//  Copyright © 2018年 long. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface MethodViewController : UIViewController

@end

/*
 http://www.cocoachina.com/ios/20180315/22625.html
 
 Method 用于表示类定义中的方法
 
 一、内部参数：
 
 typedef struct objc_method *Method;
 
 struct objc_method {
     SEL _Nonnull method_name OBJC2_UNAVAILABLE;
     char * _Nullable method_types  OBJC2_UNAVAILABLE;
     IMP _Nonnull method_imp  OBJC2_UNAVAILABLE;
 }
 
 
 
 Method 里面保存了三个参数：
 1、方法的名称SEL：选择器表示一个方法的selector的指针，可以理解为Method中的ID类型
     typedef struct objc_selector *SEL;
     objc_selector编译时会根据每个方法名字参数序列生成唯一标识
 
     获取SEL的三个方法：
     (1)、sel_registerName函数
     (2)、Objective-C编译器提供的@selector()
     (3)、NSSelectorFromString()方法
 
 2、方法的类型
 3、方法的具体实现，由 IMP 指针指向：是函数指针，指向方法的首地址，通过SEL快速得到对应IMP，这时可以跳过Runtime消息传递机制直接执行函数，比直接向对象发消息高效
 
 二、消息转发流程
 对象调 实例方法 的大致实现：
 1、先被编译成 objc_msgSend(people, sel_registerName("init"));
 2、沿着对象的 isa 指针，获得类的结构体，先在Cache里找，找到就执行，没找到就在分发列表里查找方法的selector
 3、接着在 类 的方法列表 methodLists 中，找到对应的 Method（objc_method）
 4、没找到就通过objc_msgSend结构体中指向父类的指针找到父类，然后在父类分发列表找，直到root class（NSObject）
 5、最后找到 Method 中的 IMP 指针，执行具体实现
 
 注意：编译器会根据情况在objc_msgSend，objc_msgSend_stret，objc_msgSendSuper，或 objc_msgSendSuper_stret四个方法中选一个调用。
      如果是传递给超类就会调用带super的函数，如果返回是数据结构而不是一个值就会调用带stret的函数。
      在i386平台返回类型为浮点消息会调用objc_msgSend_fpret函数。
 例子：NSStringFromClass([self class])和NSStringFromClass([super class])输出都是self的类名
 原因：调用[self class]的时候先调用objc_msgSend，发现self没有class这个方法，然后用objc_msgSendSuper就去父类找，还是没有，继续用objc_msgSendSuper到NSObject里找，结果找到了，查找NSObject中class方法的runtime源码会发现它会返回self，所以[self class]会返回self本身。
     同理[super class]相对前者就是少了objc_msgSend这一步，最后也会找到NSObject根类里的class方法，自然结果也是返回了self。
  补充：
 objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
 objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
 
 struct objc_super {
         /// Specifies an instance of a class.
         __unsafe_unretained _Nonnull id receiver;
 
         /// Specifies the particular superclass of the instance to message.
         #if !defined(__cplusplus)  &&  !__OBJC2__
        __unsafe_unretained _Nonnull Class class;
        #else
        __unsafe_unretained _Nonnull Class super_class;
        #endif
    };
 
 三、Method转发机制：（分为三步）
 1、动态方法解析：
     + (BOOL)resolveInstanceMethod:(SEL)sel {} (实例方法)
     + (BOOL)resolveClassMethod:(SEL)sel {}  (类方法)
 2、重定向接收者：
    如果无法处理消息会继续调用下面的方法，同时在这里Runtime系统实际上是给了一个替换消息接收者的机会，
    但是替换的对象千万不要是self，那样会进入死循环。
    - (id)forwardingTargetForSelector:(SEL)aSelector
 3、最后进行转发：
    如果以上两种都没法处理未知消息就需要完整消息转发了：调用如下方法
    //这一步是最后机会将消息转发给其它对象，对象会将未处理的消息相关的selector，target和参数都封装在anInvocation中。forwardInvocation:像未知消息分发中心，将未知消息转发给其它对象。注意的是forwardInvocation:方法只有在消息接收对象无法正常响应消息时才被调用。
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    //必须重写这个方法，消息转发使用这个方法获得的信息创建NSInvocation对象。
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
 
 */


/*
 https://tech.meituan.com/DiveIntoMethodCache.html
 深入理解Objective-C：方法缓存
 
 方法的缓存策略：
 一、struct objc_cache * _Nonnull cache
 
 struct objc_cache {
 uintptr_t mask;             total = mask + 1
 uintptr_t occupied;
 cache_entry *buckets[1];
 };
 objc_cache的定义看起来很简单，它包含了下面三个变量：
 1)、mask：可以认为是当前能达到的最大index（从0开始的），所以缓存的size（total）是mask+1
 2)、occupied：被占用的槽位，因为缓存是以散列表的形式存在的，所以会有空槽，而occupied表示当前被占用的数目
 3)、buckets：用数组表示的hash表，cache_entry类型，每一个cache_entry代表一个方法缓存
 (buckets定义在objc_cache的最后，说明这是一个可变长度的数组)
 
 二、cache_entry的定义如下：
 typedef struct {
 SEL name;     // same layout as struct old_method
 void *unused;
 IMP imp;  // same layout as struct old_method
 } cache_entry;
 
 cache_entry定义也包含了三个字段，分别是：
 1)、name，被缓存的方法名字
 2)、unused，保留字段，还没被使用。
 3)、imp，方法实现
 
 缓存的存储使用了散列表。
 为什么要用散列表呢？因为散列表检索起来更快
 
 三、方法缓存存在什么地方？
 struct objc_class {
 Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
 
 #if !__OBJC2__
 Class _Nullable super_class                              OBJC2_UNAVAILABLE;
 const char * _Nonnull name                               OBJC2_UNAVAILABLE;
 long version                                             OBJC2_UNAVAILABLE;
 long info                                                OBJC2_UNAVAILABLE;
 long instance_size                                       OBJC2_UNAVAILABLE;
 struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
 struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
 struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
 struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
 #endif
 }
 我们看到在类的定义里就有cache字段，没错，类的所有缓存都存在metaclass上，所以每个类都只有一份方法缓存，而不是每一个类的object都保存一份。
 
 四、
 
 
 */
